﻿using System;
using System.Collections.Generic;
using System.Net;
using WowDotNetAPI.Exceptions;
using WowDotNetAPI.Models;
using WowDotNetAPI.Utilities;

namespace WowDotNetAPI
{
    public partial class WowExplorer : IExplorer
    {
        #region Character
        public void GetCharacter(string realm, string name, Action<WebResultValue<Character>> callback)
        {
            GetCharacter(Region, realm, name, CharacterOptions.None, callback);
        }

        public void GetCharacter(Region region, string realm, string name, Action<WebResultValue<Character>> callback)
        {
            GetCharacter(region, realm, name, CharacterOptions.None, callback);
        }

        public void GetCharacter(string realm, string name, CharacterOptions characterOptions, Action<WebResultValue<Character>> callback)
        {
            GetCharacter(Region, realm, name, characterOptions, callback);
        }

        public void GetCharacter(Region region, string realm, string name, CharacterOptions characterOptions, Action<WebResultValue<Character>> callback)
        {
            TryGetData<Character>(BaseAPIurl
                + string.Format(CharacterUtility.basePath + "{0}/{1}", realm, name)
                + GetLocaleQuery()
                + CharacterUtility.buildOptionalQuery(characterOptions), callback);
        }

        #endregion

        #region Guild

        public void GetGuild(string realm, string name, Action<WebResultValue<Guild>> callback)
        {
            GetGuild(Region, realm, name, GuildOptions.None, callback);
        }

        public void GetGuild(Region region, string realm, string name, Action<WebResultValue<Guild>> callback)
        {
            GetGuild(region, realm, name, GuildOptions.None, callback);
        }

        public void GetGuild(string realm, string name, GuildOptions realmOptions, Action<WebResultValue<Guild>> callback)
        {
            GetGuild(Region, realm, name, realmOptions, callback);
        }

        public void GetGuild(Region region, string realm, string name, GuildOptions realmOptions, Action<WebResultValue<Guild>> callback)
        {
            TryGetData<Guild>(BaseAPIurl
                + string.Format(GuildUtility.basePath + "{0}/{1}", realm, name)
                + GetLocaleQuery()
                + GuildUtility.buildOptionalQuery(realmOptions), callback);
        }

        #endregion

        #region Realms

        public void GetRealms(Action<WebResultValue<IEnumerable<Realm>>> callback)
        {
            GetRealms(Region, callback);
        }

        public void GetRealms(Region region, Action<WebResultValue<IEnumerable<Realm>>> callback)
        {
            TryGetData<RealmsData>(BaseAPIurl + RealmUtility.basePath + GetLocaleQuery(),
                  realmsData => callback(new WebResultValue<IEnumerable<Realm>>(realmsData.Result.Realms, realmsData.Error) { ErrorInfo = realmsData.ErrorInfo }));
        }

        #endregion

        #region Auctions

        public void GetAuctions(string realm, Action<WebResultValue<Auctions>> callback)
        {
            TryGetData<Auctions>(string.Format(BaseAPIurl + string.Format(AuctionUtility.basePath, realm.ToLower().Replace(' ', '-'))
                + GetLocaleQuery()), callback);
        }

        #endregion

        #region Items

        public void GetItem(string id, Action<WebResultValue<Item>> callback)
        {
            TryGetData<Item>(BaseAPIurl + string.Format(ItemUtility.basePath, id) + GetLocaleQuery(), callback);
        }

        #endregion

        #region CharacterRaceInfo

        public void GetCharacterRaces(Action<WebResultValue<IEnumerable<CharacterRaceInfo>>> callback)
        {
            GetCharacterRaces(Region, callback);
        }

        public void GetCharacterRaces(Region region, Action<WebResultValue<IEnumerable<CharacterRaceInfo>>> callback)
        {
            TryGetData<CharacterRacesData>(BaseAPIurl + DataUtility.characterRacesPath + GetLocaleQuery(),
                charRacesData => callback(new WebResultValue<IEnumerable<CharacterRaceInfo>>(charRacesData.Result.Races, charRacesData.Error) { ErrorInfo = charRacesData.ErrorInfo }));
        }

        #endregion

        #region CharacterClassInfo

        public void GetCharacterClasses(Action<WebResultValue<IEnumerable<CharacterClassInfo>>> callback)
        {
            GetCharacterClasses(Region, callback);
        }

        public void GetCharacterClasses(Region region, Action<WebResultValue<IEnumerable<CharacterClassInfo>>> callback)
        {
            TryGetData<CharacterClassesData>(BaseAPIurl + DataUtility.characterClassesPath + GetLocaleQuery(),
                characterClasses => callback(new WebResultValue<IEnumerable<CharacterClassInfo>>(characterClasses.Result.Classes, characterClasses.Error) { ErrorInfo = characterClasses.ErrorInfo }));
        }

        #endregion

        #region GuildRewardInfo
        public void GetGuildRewards(Action<WebResultValue<IEnumerable<GuildRewardInfo>>> callback)
        {
            GetGuildRewards(Region, callback);
        }

        public void GetGuildRewards(Region region, Action<WebResultValue<IEnumerable<GuildRewardInfo>>> callback)
        {
            TryGetData<GuildRewardsData>(BaseAPIurl + DataUtility.guildRewardsPath + GetLocaleQuery(),
                guildRewardsData => callback(new WebResultValue<IEnumerable<GuildRewardInfo>>(guildRewardsData.Result.Rewards, guildRewardsData.Error) { ErrorInfo = guildRewardsData.ErrorInfo }));
        }

        #endregion
        #region GuildPerkInfo
        public void GetGuildPerks(Action<WebResultValue<IEnumerable<GuildPerkInfo>>> callback)
        {
            GetGuildPerks(Region, callback);
        }

        public void GetGuildPerks(Region region, Action<WebResultValue<IEnumerable<GuildPerkInfo>>> callback)
        {
            TryGetData<GuildPerksData>(BaseAPIurl + DataUtility.guildPerksPath + GetLocaleQuery(),
                guildPerksData => callback(new WebResultValue<IEnumerable<GuildPerkInfo>>(guildPerksData.Result.Perks, guildPerksData.Error) { ErrorInfo = guildPerksData.ErrorInfo }));
        }
        #endregion

        private void GetData<T>(string url, Action<WebResultValue<T>> callback) where T : class
        {
            JsonUtility.FromJSON(WebClient, url, callback);
        }

        private void TryGetData<T>(string url, Action<WebResultValue<T>> callback) where T : class
        {
            try
            {
                if (!string.IsNullOrEmpty(privateAuthKey) && !string.IsNullOrEmpty(publicAuthKey))
                {
                    JsonUtility.FromJSON<T>(url, publicAuthKey, privateAuthKey, cb => callback(new WebResultValue<T>(cb.Result, cb.Error)));
                }

                JsonUtility.FromJSON<T>(url, cb => callback(new WebResultValue<T>(cb.Result, cb.Error)));
            }
            catch (WowException wowEx)
            {
                ErrorInfo = new ErrorInfo
                {
                    ErrorDetail = wowEx.ErrorDetail,
                    OriginalException = wowEx.OriginalException,
                    RequestUrl = wowEx.Url
                };
                callback(new WebResultValue<T>(default(T), wowEx) { ErrorInfo = this.ErrorInfo });
            }
            catch (WebException webEx)
            {
                ErrorInfo = new ErrorInfo
                {
                    ErrorDetail = null,
                    OriginalException = webEx,
                    RequestUrl = string.Empty
                };
                callback(new WebResultValue<T>(default(T), webEx) { ErrorInfo = this.ErrorInfo });
            }
            catch (Exception ex)
            {
                ErrorInfo = new ErrorInfo
                {
                    ErrorDetail = null,
                    OriginalException = ex,
                    RequestUrl = string.Empty
                };
                callback(new WebResultValue<T>(default(T), ex) { ErrorInfo = this.ErrorInfo });
            }
        }
    }
}
